package social.server.ranking;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import social.server.main.User;

/**
 * Classe responsavel por administrar e manter o Objeto referente ao ranking
 * global dos usuarios. Pode ser acessada apenas por "System". Eh uma especie de
 * Singleton sua instancia nao eh recuperada pois ela nao sua instancia em si
 * nao importa a ninguem apenas as funcionalidades que desempenha.
 * 
 * @author Dimitre Oliveira.
 * 
 */
public class Ranking {

	private static LinkedList<RankedUser> ranking = new LinkedList<RankedUser>();

	/**
	 * Retorna o ranking global dos usuarios.
	 * 
	 * @return Uma lista composta por objetos "RankedUser" representando o
	 *         ranking dos usuarios.
	 */
	public List<RankedUser> getGlobalRanking() {
		return ranking;
	}

	/**
	 * Incrementa a reputacao de um "RankedUser" objeto esse que representa um
	 * usuario no ranking global. Quando um usuario tem sua reputacao
	 * incrementada o ranking se atualiza automaticamente (por meio do metodo
	 * "refreshRanking()") desse modo mantendo sempre o ranking ordenado.
	 * 
	 * @param userLogin
	 *            Login do usuario o qual se deseja incrementar a reputacao.
	 */
	public void increaseUserReputation(String userLogin) {
		Iterator<RankedUser> it = ranking.iterator();
		int index = 0;
		boolean found = false;
		while (it.hasNext() && found == false) {
			RankedUser user = it.next();
			if (user.getLogin().equals(userLogin)) {
				user.increaseReputation();
				found = true;
				if (index < ranking.size() - 1) {
					refreshRanking(index);
				}
			}
			index++;
		}
	}

	/**
	 * Ordena o ranking, atualizando a posicao de um certo usuario que teve sua
	 * reputacao incrementada. (Usado para dar suporte ao metodo
	 * "increaseUserReputatio()");
	 * 
	 * @param index
	 *            Index do usuario que teve sua reputacao incrementada.
	 */
	private void refreshRanking(int index) {
		while (index < ranking.size() - 1) {
			RankedUser nextUser = ranking.get(index + 1);
			RankedUser user = ranking.get(index);
			if (user.getReputation() > nextUser.getReputation()) {
				ranking.set(index + 1, user);
				ranking.set(index, nextUser);
			}
			index++;
		}
	}

	/**
	 * Adiciona um objeto "Ranked User", representando um usuario, na lista do
	 * ranking.
	 * 
	 * @param userLogin
	 *            Login do usuario a ser representado.
	 */
	public void addRankedUser(String userLogin) {
		ranking.addLast(new RankedUser(userLogin));
	}

	/**
	 * Retorna o ranking referente aos amigos de um usuario especifico.
	 * 
	 * @param user
	 *            Usuario referencia para o ranking.
	 * @return Uma lista de "RankedUser"s contendo os amigos do usuario
	 *         orndenados, com base em suas reputacoes(ordenados de forma
	 *         crescente, o usuario esta contido no resultado).
	 * @throws Exception
	 *             Lanca exception ao usar outros metodos.
	 */
	public List<RankedUser> getFriendsRanking(User user) throws Exception {
		List<RankedUser> rankedList = new LinkedList<RankedUser>();

		Iterator<String> it = user.getFriendList().iterator();

		rankedList.add(new RankedUser(user.getLogin(), user.getReputation()));

		while (it.hasNext()) {
			String login = it.next();
			rankedList.add(new RankedUser(login, social.server.main.System.getInstance()
					.getUserReputation(login)));
		}
		return decreasingSort(rankedList);
	}

	/**
	 * Ordena uma lista composta por "RankedUser"s de forma crescente(Ultiliza
	 * estrategia do bubbleSort).
	 * 
	 * @param list
	 *            Lista a ser ordenada.
	 * @return Uma lista ordenada.
	 */
	public static List<RankedUser> decreasingSort(List<RankedUser> list) {
		int count = 0;

		RankedUser firstElement;
		RankedUser nextElement;

		while (count < list.size()) {
			for (int index = 1; index < list.size(); index++) {
				firstElement = list.get(index - 1);
				nextElement = list.get(index);
				if (firstElement.getReputation() > nextElement.getReputation()) {
					list.set(index - 1, nextElement);
					list.set(index, firstElement);
				}
			}
			count++;
		}
		return list;
	}

	/**
	 * Reseta o ranking, apagando o objeto representante do ranking global.
	 */
	public void clear() {
		ranking.clear();
	}
}
